﻿using Autodesk.AutoCAD.DatabaseServices;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.Geometry;
using System;
using Autodesk.AutoCAD.ApplicationServices;

namespace Galaxy.AutoCAD.Databases
{
    #region 定义动态块属性类型枚举值
    public enum DynamicBlockPropertyTypeCode
    {
        String = 1,
        Real = 40,
        Short = 70,
        Long = 90
    }
    #endregion
    public static class BlockTool
    {
        #region 判断块表中有没有该块名（定义为database的括展函数）
        public static bool HasBlockName(this Database db, string blockName)
        {
            bool result = false;
            using (Transaction tran = db.TransactionManager.StartTransaction())
            {
                try
                {
                    BlockTable bt = tran.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                    result = bt.Has(blockName);
                    tran.Commit();
                }
                catch
                {
                    tran.Abort();
                }
            }
            return result;
        }
        #endregion
        #region 创建一个块并添加到数据库中（定义为database的括展函数）
        public static ObjectId AddBlockTableRecord(this Database db, string blockName, List<Entity> ents)
        {
            ObjectId result = ObjectId.Null;
            using (Transaction tran = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)tran.GetObject(db.BlockTableId, OpenMode.ForRead);
                if (!bt.Has(blockName))
                {
                    BlockTableRecord btr = new BlockTableRecord();
                    btr.Name = blockName;
                    ents.ForEach(ent => btr.AppendEntity(ent));
                    bt.UpgradeOpen();
                    result = bt.Add(btr);
                    tran.AddNewlyCreatedDBObject(btr, true);
                }
                tran.Commit();
            }
            return result;
        }
        public static ObjectId AddBlockTableRecord(this Database db, string blockName, params Entity[] ents)
        {
            return AddBlockTableRecord(db, blockName, ents.ToList());
        }
        #endregion
        #region 获得当前图形中所有的块的块名（定义为database的括展函数）
        public static List<string> GetAllBlockName(this Database db)
        {
            List<string> blockNames = new List<string>();
            using (Transaction tran = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = tran.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                foreach (ObjectId blockId in bt)
                {
                    BlockTableRecord btr = blockId.GetObject(OpenMode.ForRead) as BlockTableRecord;
                    blockNames.Add(btr.Name);
                }
                tran.Commit();
            }
            return blockNames;
        }
        #endregion
        #region 根据块名获得块（定义为database的括展函数）
        public static ObjectId GetBlockId(this Database db, string blockName)
        {
            ObjectId result = ObjectId.Null;
            if (db.HasBlockName(blockName))
            {
                using (Transaction tran = db.TransactionManager.StartTransaction())
                {
                    BlockTable bt = tran.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                    result = bt[blockName];
                    tran.Commit();
                }
            }
            return result;
        }
        #endregion
        #region 向模型空间插入块参照（定义为database的括展函数）
        public static ObjectId InsertBlockReference(this Database db, string layerName, string blockName, Point3d position, Scale3d scale, double rotateAngle)
        {
            ObjectId result = ObjectId.Null;
            using (Transaction tran = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = tran.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                if (!bt.Has(blockName))
                {
                    result = ObjectId.Null;
                }
                else
                {
                    BlockTableRecord btr = tran.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                    BlockReference br = new BlockReference(position, bt[blockName]);
                    br.ScaleFactors = scale;
                    br.Layer = layerName;
                    br.Rotation = rotateAngle;
                    result = btr.AppendEntity(br);
                    tran.AddNewlyCreatedDBObject(br, true);
                }
                tran.Commit();
            }
            return result;
        }
        public static ObjectId InsertBlockReference(this Database db, string layerName, string blockName, Point3d position, Scale3d scale, double rotateAngle,Dictionary<string,string> attNameValues)
        {
            ObjectId result = ObjectId.Null;
            using (Transaction tran = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = tran.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                if (!bt.Has(blockName))
                {
                    result = ObjectId.Null;
                }
                else
                {
                    BlockTableRecord btr = tran.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
                    BlockReference br = new BlockReference(position, bt[blockName]);
                    br.ScaleFactors = scale;
                    br.Layer = layerName;
                    br.Rotation = rotateAngle;
                    result = btr.AppendEntity(br);
                    if (btr.HasAttributeDefinitions)
                    {
                        foreach (ObjectId id in btr)
                        {
                            AttributeDefinition attDef = id.GetObject(OpenMode.ForRead) as AttributeDefinition;
                            if (attDef != null)
                            {
                                AttributeReference attribute = new AttributeReference();
                                attribute.SetAttributeFromBlock(attDef, br.BlockTransform);
                                attribute.Position = attDef.Position.TransformBy(br.BlockTransform);
                                attribute.Rotation = attDef.Rotation;
                                attribute.AdjustAlignment(db);
                                if (attNameValues.ContainsKey(attDef.Tag.ToUpper()))
                                {
                                    attribute.TextString = attNameValues[attDef.Tag.ToUpper()].ToString();
                                }
                                br.AttributeCollection.AppendAttribute(attribute);
                                db.TransactionManager.AddNewlyCreatedDBObject(attribute, true);
                            }
                        }
                    }
                    tran.AddNewlyCreatedDBObject(br, true);
                }
                tran.Commit();
            }
            return result;
        }
        #endregion
        #region 更新快参照中的属性值
        public static void UpdateAttributesInBlock(this ObjectId blockRefId, Dictionary<string, string> attNameValues)
        {
            BlockReference blockRef = blockRefId.GetObject(OpenMode.ForRead) as BlockReference;
            if (blockRef != null)
            {
                foreach (ObjectId id in blockRef.AttributeCollection)
                {
                    AttributeReference attref = id.GetObject(OpenMode.ForRead) as AttributeReference;
                    if (attNameValues.ContainsKey(attref.Tag.ToUpper()))
                    {
                        attref.UpgradeOpen();
                        attref.TextString = attNameValues[attref.Tag.ToUpper()].ToString();
                        attref.DowngradeOpen();
                    }
                }
            }
        }
        #endregion
        #region 为块表记录添加属性
        public static void AddAttsToBlock(this ObjectId blockId, List<AttributeDefinition> atts)
        {
            Database db = Application.DocumentManager.MdiActiveDocument.Database;
            using (Transaction tran = db.TransactionManager.StartTransaction())
            {
                BlockTableRecord btr = blockId.GetObject(OpenMode.ForWrite) as BlockTableRecord;
                foreach (AttributeDefinition att in atts)
                {
                    btr.AppendEntity(att);
                    db.TransactionManager.AddNewlyCreatedDBObject(att, true);
                }
                btr.DowngradeOpen();
                tran.Commit();
            }
        }
        public static void AddAttsToBlock(this ObjectId blockId, params AttributeDefinition[] atts)
        {
            blockId.AddAttsToBlock(atts.ToList());
        }
        #endregion
        #region 获取动态块的动态属性值(定义为objectid的括展函数)
        public static string GetDynamicBlockValue(this ObjectId blockId, string propName)
        {
            string propValue = null;
            var props = blockId.GetDynamicProperties();
            foreach (DynamicBlockReferenceProperty prop in props)
            {
                if (prop.PropertyName == propName)
                {
                    propValue = prop.Value.ToString();
                    break;
                }
            }
            return propValue;
        }
        #endregion
        #region 获得动态块的所有动态属性值(定义为objectid的括展函数)
        public static DynamicBlockReferencePropertyCollection GetDynamicProperties(this ObjectId blockId)
        {
            BlockReference br = blockId.GetObject(OpenMode.ForRead) as BlockReference;
            if (br == null && !br.IsDynamicBlock)
            {
                return null;
            }
            return br.DynamicBlockReferencePropertyCollection;
        }
        #endregion
        #region 设置动态块的动态属性(定义为objectid的括展函数)
        public static void SetDynamicBlockValue(this ObjectId blockId, string propName, object value)
        {
            var props = blockId.GetDynamicProperties();
            foreach (DynamicBlockReferenceProperty prop in props)
            {
                if (prop.ReadOnly == false && prop.PropertyName == propName)
                {
                    switch (prop.PropertyTypeCode)
                    {
                        case (short)DynamicBlockPropertyTypeCode.Short:
                            prop.Value = Convert.ToInt16(value);
                            break;
                        case (short)DynamicBlockPropertyTypeCode.Long:
                            prop.Value = Convert.ToInt64(value);
                            break;
                        case (short)DynamicBlockPropertyTypeCode.Real:
                            prop.Value = Convert.ToDouble(value);
                            break;
                        default:
                            prop.Value = value;
                            break;
                    }
                    break;
                }
            }
        }
        #endregion
    }
}
